// Sublib1.c : Defines the exported functions for the DLL application.
//
/*
 * Based on AVCHD2SRT-core.c obtained as noted below

 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Modified version of code from Henry Devettens - see comments below
 * exports 2 functions - createsub(char* filename, int cameratype, char token)
 * and freesub(char *pointer)
 *
 * this dll may be safe for use in multithreaded cases. It no longer relies on
 * global variables that are set when the library is loaded and persist accross calls
 * to the library. However it hasn't been tested in multithreaded use.
 *
 * It also relies on the win32 version of FFmpeg library dlls
 *
 * #define inline _inline - added at the beginning to consume the FFMPEG c code
 * #define snprintf _snprintf  - added at the beginning to consume the avchd2srt c code
 *
 * Used FFMPEG libraries from: http://ffmpeg.arrozcru.org/autobuilds/ffmpeg/mingw32/dev/shared/ffmpeg-r26400-swscale-r32676-mingw32-shared-dev.7z
 * to enable the build - you need the include and lib directores to build
 * FFMPEG dlls needed to run are from: http://ffmpeg.arrozcru.org/autobuilds/ffmpeg/mingw32/shared/ffmpeg-r26400-swscale-r32676-mingw32-shared.7z
 * to actually run the binary - you need all the dlls 
 *
 * setting up visual studio express 2010 to use FFMPEG:
 * for the project properties - 
 * under c/c++ general - add include folder from links above to "additional include directories"
 * under linker general - add lib folder from links above to "additional libary directories"
 * under linker input - add each of the FFMPEG libs to the "additional dependencies" - avcodec.lib;avcore.lib;avdevice.lib;avfilter.lib;avformat.lib;avutil.lib
 * 
 * The FFMPEG dlls (second link from above) need to be present in the executable directory along with the dll created by this code to use this library
 */
#include "stdafx.h"


/*
 *
 * AVCHD2SRT-core.c
 *
 * Version 0.5
 *
 * FFMPEG-based program to extract the time/date/geo information from the h264 video stream
 * as generated by e.g. Sony HD camcorders.
 *
 * This program runs independently, but should be distributed together with a script called
 * AVCHD2SRT, while will allow for batch operation and also adds address information.
 *
 * See the file "00 README" for background and references
 *
 * Copyright (c) 2010, 2011 Henry Devettens henryd65@gmail.com
 *
 * Compilation instructions for linux:
 *
 * 1. get and compile the ffmpeg-0.6.1 package
 * 2. in the ffmpeg-0.6.1 main directory (assume /home/me/ffmpeg-0.6.1), compile this program: gcc -O3 -L"/home/me/ffmpeg-0.6.1"/libavdevice -L"/home/me/ffmpeg-0.6.1"/libavformat -L"/home/me/ffmpeg-0.6.1"/libavcodec -L"/home/me/ffmpeg-0.6.1"/libavutil -I"/home/me/ffmpeg-0.6.1" -o avchd2srt-core avchd2srt-core.c -lavutil -lavformat -lavcodec -lz -lavutil -lm  
 *    (if the ffmpeg libs have been installed as root, the -L and -I params are not needed)
 *
 * On windows/mingw, I used
 *   ./configure --enable-memalign-hack --extra-cflags=-I/local/include --extra-ldflags=-L/local/lib
 * to configure, copied "avchd2srt.c" over ffmpeg.c and typed "make ffmpeg.exe" to create
 * the executable.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

const char program_name[] = "avchd2srt-core";
const int program_birth_year = 2010;

#define inline _inline
#define snprintf _snprintf
//#include <malloc.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"

static const uint8_t avchd_mdpm[] =
  { 0x17,0xee,0x8c,0x60,0xf8,0x4d,0x11,0xd9,0x8c,0xd6,0x08,0x00,0x20,0x0c,0x9a,0x66,
    'M','D','P','M' };

char * weekday[] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};

char * monthname[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
#define BCD_2_INT(c) ((c >> 4)*10+(c&0xf))

//adding definitions for data structures to store full metadata here

typedef struct metastruct {
	int frameno;
	int p_flags;
	__int64 p_pts;
	__int64 p_dts;
	__int64 p_pos;
	int p_dur;
	unsigned char* framedata;
} metaframe;


//to here

//int srt=-1;			// srt counter
//char srtT[256], srtTn[256];	// srt text: current and new
//int srtTi=0, srtTni=0;		// index in srtT ans srtTn arrays for next addition
//time_t secsince1970, srtTsec=0;	    // Tricky: if new sub is one sec before current, ignore.
//struct tm t;			// to calculate srtTsec
//time_t start_time, end_time;	    // to measure the duration per file
//char SL;			// will be + for above and - for below sea level
//long int srtTimer;		// start time (in milliseconds) for current srtT
//int srtH, srtM, srtS, srtmS;	// Hour, Minute, Seconds, milli
//int foundgeo;
//int frm;			// frame counter
//int fps_den, fps_num;		// for the frame rate
//float fps;
//char fileout[1024];		// output file name
////FILE *filesrt;			    // filepointer to the output file -- comment out for use in library
//int alignct = 0; // added - used to keep track of the output string position




//static void set_srt_hmsm() {
//  srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
//  srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
//  srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
//}

//start editing the output in this function
/*static void print_one_srt_entry(char* storagestr, char token) {   //merged messy version
  //set_srt_hmsm();
  //add set_srt_hmsm here
  srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
  srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
  srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
  //done add

  //fprintf(filesrt, "%0d\n", srt+1);   //comment out for use in library
  alignct += snprintf (&(storagestr[alignct]), 256, "%0d\n", srt+1);   //added - print to storage buffer
  //fprintf(filesrt, "%02d:%02d:%02d,%03d --> ", srtH, srtM, srtS, srtmS);  //comment out for use in library
  alignct += snprintf (&(storagestr[alignct]), 256, "%02d:%02d:%02d,%03d --> ", srtH, srtM, srtS, srtmS);  //added - print to storage buffer
  srtTimer = (long int) (((long int)(frm)*1000L)/fps);
  //set_srt_hmsm();
  //add set_srt_hmsm here
  srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
  srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
  srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
  //done add

  //fprintf(filesrt, "%02d:%02d:%02d,%03d\n", srtH, srtM, srtS, srtmS);  //comment out for use in library
  alignct += snprintf (&(storagestr[alignct]), 256, "%02d:%02d:%02d,%03d\n", srtH, srtM, srtS, srtmS);   //added - print to storage buffer
  //fprintf(filesrt, "%s\n", srtT);  //comment out for use in library
  //alignct += snprintf (&(storagestr[alignct]), 256, "%s\n", srtT);  //added - print to storage buffer - does not add separateer character
  alignct += snprintf (&(storagestr[alignct]), 256, "%s%c", srtT,token);  //added - print to storage buffer - adds the separator character after each subtitle
  srtTi = snprintf(srtT, 256, "");
}*/



//adding code to free the memory here

__declspec(dllexport) void freesub(char* pointer) {
	free (pointer); 
	return;
}


__declspec(dllexport) char* createsub(char* filename, int cameratype, char token) {

	AVFormatContext *pFormatCtx;
  int strm;
  int videoStream;
  AVCodecContext *pCodecCtx;
  AVCodec *pCodec;
  AVFrame *pFrame;
  int frameFinished=1;
  AVPacket packet;
  uint8_t * ptr;
  int j;
  int tag, num_tags, i;
  int year, month, day, hour, minute, second, tz; 	// ..., timezone
  int latH, latD, latM, latS; 				// latitude hemi, deg, min, sec
  int lonE, lonD, lonM, lonS;               		// longitude "east", deg, min, sec
  int altS, altL, altD;                               	// altitude below/above sea, level, divisor
  int speed, speD, speU;				// speed, speed divisor, speed unit
  unsigned long duration = 0;
  char* bufferptr;   //this is the pointer to the actual results string

 // adding previous globals here

	int srt=-1;			// srt counter
	char srtT[256], srtTn[256];	// srt text: current and new
	int srtTi=0, srtTni=0;		// index in srtT ans srtTn arrays for next addition
	time_t secsince1970, srtTsec=0;	    // Tricky: if new sub is one sec before current, ignore.
	struct tm t;			// to calculate srtTsec
	time_t start_time, end_time;	    // to measure the duration per file
	char SL;			// will be + for above and - for below sea level
	long int srtTimer;		// start time (in milliseconds) for current srtT
	int srtH, srtM, srtS, srtmS;	// Hour, Minute, Seconds, milli
	int foundgeo;
	int frm;			// frame counter
	int fps_den, fps_num;		// for the frame rate
	float fps;
	char fileout[1024];		// output file name
	int alignct = 0; // added - used to keep track of the output string position

	//done with previous globals here


  fprintf(stderr, "Input file name to dll is %s\n", filename);

  av_register_all();


  /* allocate the media context */
  pFormatCtx = avformat_alloc_context();
  if (!pFormatCtx) {
      fprintf(stderr, "Memory error\n");
	  return ("memory_error");
  }

  // Open video file
  if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0) {
    fprintf(stderr, "Cannot open file %s.\n", filename);
	return ("file_open_error");
  }

  //if (set_output_file(argv[1])) return -1;   //comment out for use in library

  // Retrieve stream information
  if(av_find_stream_info(pFormatCtx)<0) {
    fprintf(stderr, "Could not find stream information\n");
	return ("no_stream_info");
  }
  duration = pFormatCtx->duration;     //obtain length of movie clip in ffmpeg units whatever they are
  duration = duration / 1000000 + 10;  //convert to seconds and add 10, just to be safe

  //bufferptr = (char*) malloc (duration * 64);  //allocate memory to store subtitle string based on duration. 64 bytes is longer than any subtitle section, but check on long movies!
  bufferptr = (char*) malloc (duration * 100);   //100 bytes per subtitle section should be sufficient.

  fprintf(stderr, "the duration var is %d\n",duration);
  // Dump information about file onto standard error:
  dump_format(pFormatCtx, 0, filename, 0);

  start_time = time (NULL);

  // Find the first video stream
  videoStream=-1;
  for(strm=0; strm<pFormatCtx->nb_streams; strm++)
    if(pFormatCtx->streams[strm]->codec->codec_type==CODEC_TYPE_VIDEO) {
      videoStream=strm;
	  //need to place the selecton "if" statements around this
	  //changed the comments from bottom to to top two
	  if (cameratype == 0) {
	  fps_num=pFormatCtx->streams[strm]->r_frame_rate.num;   //version 5
	  fps_den=pFormatCtx->streams[strm]->r_frame_rate.den;   //version 5
	  fps = (float) fps_num / fps_den;  //version 5
	  }
	  else if (cameratype == 1) {
		fps_num=pFormatCtx->streams[strm]->codec->time_base.num; //version 4 //seems to work
		fps_den=pFormatCtx->streams[strm]->codec->time_base.den; //version 4 //seems to work	  
		//fps = (float) fps_den / fps_num / 2.0;   //copyied from version 4  //works but 2.0 wrong
		fps = (float) fps_den / fps_num;   //copyied from version 4 but edited  //this seems to work!
	  }
	  else {
		  return ("camera_not_supported_error");
	  }
	 
      fprintf(stderr, "  Frame rate: %2.2f (%d/%d)\n", fps, fps_num, fps_den);
      break;
    }

  fprintf(stderr, "  Output file name: \'%s\'\n", fileout);

  if(videoStream==-1) {
    fprintf(stderr, "Did not find a video stream\n");
    //return -1; // Didn't find a video stream  //commented out for use in library
	return ("videostream_not_found_error");
  }

  // Get a pointer to the codec context for the video stream
  pCodecCtx=pFormatCtx->streams[videoStream]->codec;

  // Find the decoder for the video stream
  pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
  if(pCodec==NULL) {
    fprintf(stderr, "Unsupported codec!\n");
    //return -1; // Codec not found  //commented out for use in library
	return ("codec_not_found_error");
  }

  // Open codec
  if(avcodec_open(pCodecCtx, pCodec)<0) {
    fprintf(stderr, "Could not open codec\n");
    //return -1; // Could not open codec  //commented out for use in library
	return ("open_codec_error");
  }

  // Allocate video frame
  pFrame=avcodec_alloc_frame();

  frm=0;
  while(av_read_frame(pFormatCtx, &packet)>=0) {
    // Is this a packet from the video stream?
    if(packet.stream_index==videoStream) {
   	  // Decode video frame

//      fprintf(stderr, "Packet size before decode: %0d\n", packet.size);    //used in version 4
//      avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,              //used in version 4
//                           packet.data, packet.size);
//      fprintf(stderr, "Packet size after decode: %0d\n", packet.size);    
//      Can we do this quicker? All we need is a correct value of frameFinished.
        

// For Sony, the meta data appears in the key frame, so we looked for it
// only in those: if ((packet.flags & PKT_FLAG_KEY) != 0) {...
// But for Panasonic this did not work. So now we just always do it:

      if (1) { 

/*          if ((packet.flags & PKT_FLAG_KEY) != 0) 
             fprintf(stderr, "Found a key frame, frame number %0d (finished: %d)\n", frm+1, frameFinished);
          else
             fprintf(stderr, "Found a non-key frame, frame number %0d (finished: %d)\n", frm+1, frameFinished); 
          av_pkt_dump(stderr, &packet, 1);
        av_hex_dump(stderr, packet.data, 48);
*/

// start looking for the message - assuming it's within the first 256 bytes of the keyframe

        year = -1; month = -1; day = -1; hour = -1; minute = -1; second = -1; tz = -1;
        latH = -1; latD = -1; latM = -1; latS = -1; 
        lonE = -1; lonD = -1; lonM = -1; lonS = -1;
        altS = -1; altL = -1; altD = -1;
        speed = -1; speD = -1; speU = -1;

        ptr = packet.data;
        j=0;
        while ((j<256) && (memcmp(ptr+j, avchd_mdpm, 20))) j++;
        if (j<256) {
//          fprintf(stderr, "Found the message at bytes %0d\n", j);
//          av_hex_dump(stderr, ptr+j, 80);

          /* Skip GUID + MDPM */
          ptr += j+20;

          num_tags = *ptr; ptr++;
          
          for(i = 0; i < num_tags; i++)
            {
            tag = *ptr; ptr++;

if (tag==0) {	// hack - hope it's right
  tag = *ptr; ptr++;
}

//            fprintf(stdout, "Tag: 0x%02x, Data: %02x %02x %02x %02x\n",
//                    tag, ptr[0], ptr[1], ptr[2], ptr[3]);

// For Panasonic, some tags appear twice in the message, screwing it up.
// Am probably not reading the message properly. Never happened for Sony.
// So now we only take the first, hence the additional checks on ==-1 below.
// It may also be needed for Geo info - need to have that tested.

            switch(tag)
              {
              case 0x18:
                if (year==-1) { tz=ptr[0];
                                year  = BCD_2_INT(ptr[1])*100 + BCD_2_INT(ptr[2]);
                                month = BCD_2_INT(ptr[3]);}
                break;
              case 0x19:
                if (day==-1) { day    = BCD_2_INT(ptr[0]); hour   = BCD_2_INT(ptr[1]);
                               minute = BCD_2_INT(ptr[2]); second = BCD_2_INT(ptr[3]);}
                break;
	      case 0xb1:
		latH   = (ptr[0]);
		break;
	      case 0xb2:
		latD   = (ptr[0])*256 + (ptr[1]);
                break;
              case 0xb3:
                latM   = (ptr[0])*256 + (ptr[1]);
                break;
              case 0xb4:
                latS   = (ptr[0])*256 + (ptr[1]);
                break;
	      case 0xb5:
		lonE   = (ptr[0]);
		break;
	      case 0xb6:
		lonD   = (ptr[0])*256 + (ptr[1]);
                break;
              case 0xb7:
                lonM   = (ptr[0])*256 + (ptr[1]);
                break;
              case 0xb8:
                lonS   = (ptr[0])*256 + (ptr[1]);
                break;
              case 0xb9:
                altS   = (ptr[0]);
                break;
              case 0xba:
                altL   = (ptr[0])*256 + (ptr[1]);
                altD   = (ptr[3]);
                break;
              case 0xc1:
                speU   = (ptr[0]);
                break;
              case 0xc2:
                speed  = (ptr[0])*256 + (ptr[1]);
                speD   = (ptr[3]);
                break;
              }
            ptr += 4;
            }
          
          srtTni=0;

          if((year >= 0) && (month >= 0) && (day >= 0) &&
             (hour >= 0) && (minute >= 0) && (second >= 0))
            {
              t.tm_year = year-1900;  t.tm_mon = month-1; t.tm_mday = day;
              t.tm_hour = hour;       t.tm_min = minute;  t.tm_sec = second;
              t.tm_isdst = 0;
              secsince1970 = mktime(&t);
			  

			  //here we edit the output format
              srtTni += snprintf(&(srtTn[srtTni]), 40, "%s %02d-%s-%04d %02d:%02d:%02d", //original code
                                weekday[t.tm_wday],                                     //original code
                                day, monthname[month-1], year, hour, minute, second);   //original code

			  //srtTni += snprintf(&(srtTn[srtTni]), 40, "%s %02d-%s-%04d", //edited code - stops short for some reason
              //                   weekday[t.tm_wday],                                     //edited code
              //                   day, monthname[month-1], year);   //edited code


              //if (tz<64) { // valid time zone value             //appears as time zone data - edited out
              //  if (tz<32) SL='+'; else SL='-';                 //edited out
              //  tz=tz%32;                                       //edited out
              //  srtTni += snprintf(&(srtTn[srtTni]), 30, " (%c%02d:%02d)",   //edited out and still works
              //                     SL, tz/2, 30*(tz%2));                     //edited out and still works
              //}                                                              //edited out and still works
              srtTni += snprintf(&(srtTn[srtTni]), 3, "\n");
            }
          foundgeo=0;
          if((latH >= 0) && (latD >= 0) && (latM >= 0) && (latS >= 0) && 
             (lonE >= 0) && (lonD >= 0) && (lonM >= 0) && (lonS >= 0))
            {
            foundgeo=1;
            srtTni += snprintf(&(srtTn[srtTni]), 60, "GPS: %0d %0d %0.2f %c %0d %0d %0.2f %c",
                               latD, latM, ((float)latS)/1000.0, latH,
                               lonD, lonM, ((float)lonS)/1000.0, lonE);
            }
          if ((altS >= 0) && (altL >= 0))
            {
            foundgeo=1;
            if (altS==0) SL='+'; else SL='-';          // Above + or Below - Sea level
            if (altD==0) altD = 10;			// stub - just assume, not sure
            if (altD==1) 
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %c%0d m", SL, altL);
            else
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %c%0.1f m", SL, ((float)altL)/(float)altD);
            }
          if ((speed>=0) && (speD>0) && (speU>0))
            {
            foundgeo=1;
            if (speD==1) 
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %0d", speed);
            else
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %0.1f", ((float)speed)/(float)speD);
            if (speU=='K')
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %s", "km/h");
            else if (speU=='M')
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %s", "mph");
            else if (speU=='N')
               srtTni += snprintf(&(srtTn[srtTni]), 30, " %s", "knots");
            }
          if (foundgeo) {
            srtTni += snprintf(&(srtTn[srtTni]), 30, "\n"); 
          }

          if ( (strcmp (srtT,srtTn) != 0) && ((srtTsec-secsince1970) != 1) ) {
							// new subtitle not the same as current
 		// and the new subtitle is not one second older than current. In latter case we assume
		// B-frames and silenty ignore for now
            //if (srtTi) print_one_srt_entry(bufferptr, token);		// so print & clear the current one if it exists
			//going to try to add the print code in here to avoid use of globals - line above will be commented out
			if (srtTi) {

				srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
				srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
				srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
				alignct += snprintf (&(bufferptr[alignct]), 256, "%0d\n", srt+1);   //added - print to storage buffer
				alignct += snprintf (&(bufferptr[alignct]), 256, "%02d:%02d:%02d,%03d --> ", srtH, srtM, srtS, srtmS);  //added - print to storage buffer

				srtTimer = (long int) (((long int)(frm)*1000L)/fps);
				srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
				srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
				srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
				alignct += snprintf (&(bufferptr[alignct]), 256, "%02d:%02d:%02d,%03d\n", srtH, srtM, srtS, srtmS);   //added - print to storage buffer
				//fprintf(filesrt, "%s\n", srtT);  //comment out for use in library
				//alignct += snprintf (&(bufferptr[alignct]), 256, "%s\n", srtT);  //added - print to storage buffer - does not add separateer character
				alignct += snprintf (&(bufferptr[alignct]), 256, "%s%c", srtT,token);  //added - print to storage buffer - adds the separator character after each subtitle
				srtTi = snprintf(srtT, 256, "");

			}
			//done adding print code



            if (srtTni) {				// and if there is a new one, make current
              srt++;
              srtTimer = (long int) (((long int)(frm)*1000L)/fps);
              srtTsec = secsince1970;
              srtTi = snprintf(srtT, 256, "%s", srtTn);
            }
          } // else if new subtitle is the same as previous, do nothing
        } else {
//          fprintf(stderr, "did not find the message\n");
        }
      }
      // Did we get a video frame?
      if(frameFinished) {
        frm++;
      }
    }
    
    // Free the packet that was allocated by av_read_frame
    av_free_packet(&packet);
  }

  //if (srtTi>0) 
  //print_one_srt_entry(bufferptr, token);
  //going to try to add the print code in here to avoid use of globals - line above will be commented out

	if (srtTi) {

		srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
		srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
		srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
		alignct += snprintf (&(bufferptr[alignct]), 256, "%0d\n", srt+1);   //added - print to storage buffer
		alignct += snprintf (&(bufferptr[alignct]), 256, "%02d:%02d:%02d,%03d --> ", srtH, srtM, srtS, srtmS);  //added - print to storage buffer
		srtTimer = (long int) (((long int)(frm)*1000L)/fps);
		srtH = (int) (srtTimer/3600000L); srtTimer -= 3600000L*(long int) srtH;
		srtM = (int) (srtTimer/60000L); srtTimer -= 60000L*(long int) srtM;
		srtS = (int) (srtTimer/1000L); srtmS = (int) (srtTimer-1000L*(long int)srtS);
		alignct += snprintf (&(bufferptr[alignct]), 256, "%02d:%02d:%02d,%03d\n", srtH, srtM, srtS, srtmS);   //added - print to storage buffer
		//fprintf(filesrt, "%s\n", srtT);  //comment out for use in library
		//alignct += snprintf (&(bufferptr[alignct]), 256, "%s\n", srtT);  //added - print to storage buffer - does not add separateer character
		alignct += snprintf (&(bufferptr[alignct]), 256, "%s%c", srtT,token);  //added - print to storage buffer - adds the separator character after each subtitle
		srtTi = snprintf(srtT, 256, "");

	}
  //done adding

  fprintf(stderr, "  Read %d frames\n", frm);

  end_time = time (NULL);

  fprintf(stderr, "Processed in %0d seconds\n", (int)difftime(end_time, start_time));

  // Close the SRT output file
  //fclose(filesrt);   //comment out for use in library

  // Free the YUV frame
  av_free(pFrame);

  // Close the codec
  avcodec_close(pCodecCtx);

  // Close the video file
  av_close_input_file(pFormatCtx);

  return (bufferptr);

}


__declspec(dllexport) void freemetaframe(char** pointer) {
	free (*pointer); 
	return;
}

__declspec(dllexport)  char * getmetaframe(char* filename, int cameratype, long* numberoftags, int* metaframelength, int* numframes, long* duration, float* framerate, unsigned char** framearray)
	{
		//int cameratype = 1;
		AVFormatContext *pFormatCtx;
		int strm;
		int videoStream;
		AVCodecContext *pCodecCtx;
		AVCodec *pCodec;
		AVFrame *pFrame;
		int frameFinished=1;
		AVPacket packet;
		uint8_t * ptr;
		int j;
		int tag, num_tags, i;
		int year, month, day, hour, minute, second, tz; 	// ..., timezone
		int latH, latD, latM, latS; 				// latitude hemi, deg, min, sec
		int lonE, lonD, lonM, lonS;               		// longitude "east", deg, min, sec
		int altS, altL, altD;                               	// altitude below/above sea, level, divisor
		int speed, speD, speU;				// speed, speed divisor, speed unit
		//unsigned long duration = 0;
		char* bufferptr;   //this is the pointer to the actual results string

		int srt=-1;			// srt counter
		char srtT[256], srtTn[256];	// srt text: current and new
		int srtTi=0, srtTni=0;		// index in srtT ans srtTn arrays for next addition
		time_t secsince1970, srtTsec=0;	    // Tricky: if new sub is one sec before current, ignore.
		struct tm t;			// to calculate srtTsec
		time_t start_time, end_time;	    // to measure the duration per file
		char SL;			// will be + for above and - for below sea level
		long int srtTimer;		// start time (in milliseconds) for current srtT
		int srtH, srtM, srtS, srtmS;	// Hour, Minute, Seconds, milli
		int foundgeo;
		int frm;			// frame counter
		int fps_den, fps_num;		// for the frame rate
		float fps;
		char fileout[1024];		// output file name
		int alignct = 0; // added - used to keep track of the output string position
		long extract_numframes;

		fprintf(stderr, "Input file name to dll is %s\n", filename);

		av_register_all();


		/* allocate the media context */
		pFormatCtx = avformat_alloc_context();
		if (!pFormatCtx) {
			fprintf(stderr, "Memory error\n");
			return ("memory_error");
		}

		// Open video file
		if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0) {
			fprintf(stderr, "Cannot open file %s.\n", filename);
			return ("file_open_error");
		}

	  //if (set_output_file(argv[1])) return -1;   //comment out for use in library

		// Retrieve stream information
		if(av_find_stream_info(pFormatCtx)<0) {
			fprintf(stderr, "Could not find stream information\n");
			return ("no_stream_info");
		}
		*duration = pFormatCtx->duration;     //obtain length of movie clip in ffmpeg units whatever they are
		//duration = duration / 1000000 + 10;  //convert to seconds and add 10, just to be safe

		//bufferptr = (char*) malloc (duration * 64);  //allocate memory to store subtitle string based on duration. 64 bytes is longer than any subtitle section, but check on long movies!
		//bufferptr = (char*) malloc (duration * 100);   //100 bytes per subtitle section should be sufficient.

		fprintf(stderr, "the duration var is %d\n",duration);
		// Dump information about file onto standard error:
		dump_format(pFormatCtx, 0, filename, 0);

		start_time = time (NULL);

		// Find the first video stream
		videoStream=-1;
		for(strm=0; strm<pFormatCtx->nb_streams; strm++)
			if(pFormatCtx->streams[strm]->codec->codec_type==CODEC_TYPE_VIDEO) {
			videoStream=strm;
			//need to place the selecton "if" statements around this
			//changed the comments from bottom to to top two
			// some testing done between here:
			extract_numframes = pFormatCtx->streams[strm]->nb_frames;
			*numframes = extract_numframes;
			// and here
			if (cameratype == 0) {
			fps_num=pFormatCtx->streams[strm]->r_frame_rate.num;   //version 5
			fps_den=pFormatCtx->streams[strm]->r_frame_rate.den;   //version 5
			fps = (float) fps_num / fps_den;  //version 5
			*framerate = fps;
			}
			else if (cameratype == 1) {
				fps_num=pFormatCtx->streams[strm]->codec->time_base.num; //version 4 //seems to work
				fps_den=pFormatCtx->streams[strm]->codec->time_base.den; //version 4 //seems to work	  
				//fps = (float) fps_den / fps_num / 2.0;   //copyied from version 4  //works but 2.0 wrong
				fps = (float) fps_den / fps_num;   //copyied from version 4 but edited  //this seems to work!
				*framerate = fps;
			}
			else {
				return ("camera_not_supported_error");
			}
	 
			fprintf(stderr, "  Frame rate: %2.2f (%d/%d)\n", fps, fps_num, fps_den);
			break;
			}

		fprintf(stderr, "  Output file name: \'%s\'\n", fileout);

		if(videoStream==-1) {
			fprintf(stderr, "Did not find a video stream\n");
			//return -1; // Didn't find a video stream  //commented out for use in library
			return ("videostream_not_found_error");
		}

		// Get a pointer to the codec context for the video stream
		pCodecCtx=pFormatCtx->streams[videoStream]->codec;

		// Find the decoder for the video stream
		pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
		if(pCodec==NULL) {
			fprintf(stderr, "Unsupported codec!\n");
			//return -1; // Codec not found  //commented out for use in library
			return ("codec_not_found_error");
		}

		// Open codec
		if(avcodec_open(pCodecCtx, pCodec)<0) {
			fprintf(stderr, "Could not open codec\n");
			//return -1; // Could not open codec  //commented out for use in library
			return ("open_codec_error");
		}

		// Allocate video frame
			pFrame=avcodec_alloc_frame();
		
		//missing tons of stuff between here:
		//start by grabbing metadata from first frame
		frm=0;
		av_read_frame(pFormatCtx, &packet);
				//Is this a packet from the video stream?
		if(packet.stream_index==videoStream) {
			
			ptr = packet.data;
			j=0;
			while ((j<256) && (memcmp(ptr+j, avchd_mdpm, 20))) j++;
				if (j<256) {
//					fprintf(stderr, "Found the message at bytes %0d\n", j);
//					av_hex_dump(stderr, ptr+j, 80);

          /* Skip GUID + MDPM */
						ptr += j+20;

						num_tags = *ptr; ptr++;
						*numberoftags = num_tags;
						*metaframelength = num_tags * 5;
						//need to allocate storage for frame here using number of tags plus tag length
						*framearray = (unsigned char *) malloc (num_tags*5+1);
						/*for(i = 0; i < num_tags; i++)
							{
								tag = *ptr; ptr++;

								if (tag==0) {	// hack - hope it's right
									tag = *ptr; ptr++;
								}

							}*/
						memcpy (*framearray,ptr,(num_tags*5));
						//framearray = ptr;
				}
				av_free_packet(&packet);
				av_free(pFrame);
				avcodec_close(pCodecCtx);
				av_close_input_file (pFormatCtx);
				
				//av_freep (pFormatCtx); //causes hard crash with no errors on .net side - never returns control
				return ("found_metaframe");
		// and here
		}
		return ("metaframe_not_found");
	}



__declspec(dllexport)  char * getmetadata(char* filename, int cameratype, int numberoftags, int metaframelength, int numberofframes, metaframe* allmetadata)     //unsigned char* metadataarray)
	{


		//int cameratype = 1;
		AVFormatContext *pFormatCtx;
		int strm;
		int videoStream;
		AVCodecContext *pCodecCtx;
		AVCodec *pCodec;
		AVFrame *pFrame;
		int frameFinished=1;
		AVPacket packet;
		uint8_t * ptr;
		int j;
		int tag, num_tags, i;
		int year, month, day, hour, minute, second, tz; 	// ..., timezone
		int latH, latD, latM, latS; 				// latitude hemi, deg, min, sec
		int lonE, lonD, lonM, lonS;               		// longitude "east", deg, min, sec
		int altS, altL, altD;                               	// altitude below/above sea, level, divisor
		int speed, speD, speU;				// speed, speed divisor, speed unit
		unsigned long duration = 0;
		char* bufferptr;   //this is the pointer to the actual results string

		int srt=-1;			// srt counter
		char srtT[256], srtTn[256];	// srt text: current and new
		int srtTi=0, srtTni=0;		// index in srtT ans srtTn arrays for next addition
		time_t secsince1970, srtTsec=0;	    // Tricky: if new sub is one sec before current, ignore.
		struct tm t;			// to calculate srtTsec
		time_t start_time, end_time;	    // to measure the duration per file
		char SL;			// will be + for above and - for below sea level
		long int srtTimer;		// start time (in milliseconds) for current srtT
		int srtH, srtM, srtS, srtmS;	// Hour, Minute, Seconds, milli
		int foundgeo;
		int frm;			// frame counter
		int fps_den, fps_num;		// for the frame rate
		float fps;
		char fileout[1024];		// output file name
		int alignct = 0; // added - used to keep track of the output string position
		int exceededtags = 0;
		int exceededframes = 0;
		int p_flags = 0;
		long p_pts = 0;
		long p_dts = 0;
		long p_pos = 0;
		int p_dur = 0;

		fprintf(stderr, "Input file name to dll is %s\n", filename);

		av_register_all();


		/* allocate the media context */
		pFormatCtx = avformat_alloc_context();
		if (!pFormatCtx) {
			fprintf(stderr, "Memory error\n");
			return ("memory_error");
		}

		// Open video file
		if(av_open_input_file(&pFormatCtx, filename, NULL, 0, NULL)!=0) {
			fprintf(stderr, "Cannot open file %s.\n", filename);
			return ("file_open_error");
		}

	  //if (set_output_file(argv[1])) return -1;   //comment out for use in library

		// Retrieve stream information
		if(av_find_stream_info(pFormatCtx)<0) {
			fprintf(stderr, "Could not find stream information\n");
			return ("no_stream_info");
		}
		duration = pFormatCtx->duration;     //obtain length of movie clip in ffmpeg units whatever they are
		duration = duration / 1000000 + 10;  //convert to seconds and add 10, just to be safe

		//bufferptr = (char*) malloc (duration * 64);  //allocate memory to store subtitle string based on duration. 64 bytes is longer than any subtitle section, but check on long movies!
		//bufferptr = (char*) malloc (duration * 100);   //100 bytes per subtitle section should be sufficient.

		fprintf(stderr, "the duration var is %d\n",duration);
		// Dump information about file onto standard error:
		dump_format(pFormatCtx, 0, filename, 0);

		start_time = time (NULL);

		// Find the first video stream
		videoStream=-1;
		for(strm=0; strm<pFormatCtx->nb_streams; strm++)
			if(pFormatCtx->streams[strm]->codec->codec_type==CODEC_TYPE_VIDEO) {
			videoStream=strm;
			//need to place the selecton "if" statements around this
			//changed the comments from bottom to to top two
			if (cameratype == 0) {
			fps_num=pFormatCtx->streams[strm]->r_frame_rate.num;   //version 5
			fps_den=pFormatCtx->streams[strm]->r_frame_rate.den;   //version 5
			fps = (float) fps_num / fps_den;  //version 5
			}
			else if (cameratype == 1) {
				fps_num=pFormatCtx->streams[strm]->codec->time_base.num; //version 4 //seems to work
				fps_den=pFormatCtx->streams[strm]->codec->time_base.den; //version 4 //seems to work	  
				//fps = (float) fps_den / fps_num / 2.0;   //copyied from version 4  //works but 2.0 wrong
				fps = (float) fps_den / fps_num;   //copyied from version 4 but edited  //this seems to work!
			}
			else {
				return ("camera_not_supported_error");
			}
	 
			fprintf(stderr, "  Frame rate: %2.2f (%d/%d)\n", fps, fps_num, fps_den);
			break;
			}

		fprintf(stderr, "  Output file name: \'%s\'\n", fileout);

		if(videoStream==-1) {
			fprintf(stderr, "Did not find a video stream\n");
			//return -1; // Didn't find a video stream  //commented out for use in library
			return ("videostream_not_found_error");
		}

		// Get a pointer to the codec context for the video stream
		pCodecCtx=pFormatCtx->streams[videoStream]->codec;

		// Find the decoder for the video stream
		pCodec=avcodec_find_decoder(pCodecCtx->codec_id);
		if(pCodec==NULL) {
			fprintf(stderr, "Unsupported codec!\n");
			//return -1; // Codec not found  //commented out for use in library
			return ("codec_not_found_error");
		}

		// Open codec
		if(avcodec_open(pCodecCtx, pCodec)<0) {
			fprintf(stderr, "Could not open codec\n");
			//return -1; // Could not open codec  //commented out for use in library
			return ("open_codec_error");
		}

		// Allocate video frame
			pFrame=avcodec_alloc_frame();
		
		//missing tons of stuff between here:
		//start by grabbing metadata from first frame
		frm=0;
		while (av_read_frame(pFormatCtx, &packet)>=0)  {

			//frm++;
			if (frm > numberofframes) {
				exceededframes = 1;
				break;
				//return ("file_exceeded_frames_allocated");
			}

				//Is this a packet from the video stream?
			if(packet.stream_index==videoStream) {
				
				ptr = packet.data;
				p_flags = packet.flags;
				p_pts = packet.pts;
				p_dts = packet.dts;
				p_pos = packet.pos;
				p_dur = packet.duration;

				j=0;
				while ((j<256) && (memcmp(ptr+j, avchd_mdpm, 20))) j++;
				if ((j<256)&&(p_dur > 0)) {
					//fprintf(stderr, "Found the message at bytes %0d\n", j);
					//av_hex_dump(stderr, ptr+j, 80);

					/* Skip GUID + MDPM */
					frm++;
					ptr += j+20;

					num_tags = *ptr; ptr++;
					if (num_tags > numberoftags) {
						exceededtags = 1;
					}
					//*numberoftags = num_tags;
					//*metaframelength = num_tags * 5;
						//need to allocate storage for frame here using number of tags plus tag length
						//*framearray = (unsigned char *) malloc (num_tags*5+1);
						/*for(i = 0; i < num_tags; i++)
							{
								tag = *ptr; ptr++;

								if (tag==0) {	// hack - hope it's right
									tag = *ptr; ptr++;
								}

							}*/

					//ptr is now positioned at the proper place on the metadata, ready to add read code.
					//memcpy (metadataarray,ptr,(num_tags*5));
					//return ("before use");
					allmetadata[frm-1].frameno = frm;
					allmetadata[frm-1].p_flags = p_flags;
					allmetadata[frm-1].p_pts = p_pts;
					allmetadata[frm-1].p_dts = p_dts;
					allmetadata[frm-1].p_pos = p_pos;
					allmetadata[frm-1].p_dur = p_dur;
					//return ("after set frameno"); //gets to here, so what gives?
					//allmetadata[frm-1].framedata = (unsigned char *)malloc (metaframelength);
					memcpy (allmetadata[frm-1].framedata,ptr,(metaframelength));   //all the trouble comes down to this. The frameno works ok.
					//memcpy (allmetadata[frm-1].framedata,ptr,2);
					//return ("after first copy");
				}
					
			}
		av_free_packet(&packet);
		}
		//av_free_packet(&packet);
		av_free(pFrame);
		avcodec_close(pCodecCtx);
		av_close_input_file (pFormatCtx);
		if (exceededtags == 1)
		{
			return ("exceeded_tags_metadata_copied");
		}
		if (exceededframes == 1)
		{
			return ("exceeded_framenumber_metadata_copied");
		}
		return ("metadata_copied");
	}